Meta-packages
authorYehuda Katz <wycats@gmail.com>
Tue, 1 Jul 2014 01:36:27 +0000 (18:36 -0700)
committerYehuda Katz <wycats@gmail.com>
Tue, 1 Jul 2014 01:36:27 +0000 (18:36 -0700)
src/cargo/core/registry.rs
src/cargo/core/source.rs
src/cargo/ops/cargo_read_manifest.rs
src/cargo/ops/cargo_rustc.rs
tests/test_cargo_compile_git_deps.rs

index f150c7880234915858b5eb022c0b278129274a07..6a2d6feff3401f4e1d51d99bfeaf227dc9126dce 100644 (file)
@@ -29,6 +29,7 @@ impl<'a> PackageRegistry<'a> {
                config: &'a mut Config<'a>) -> CargoResult<PackageRegistry<'a>> {
 
         let mut reg = PackageRegistry::empty(config);
+        let source_ids = dedup(source_ids);
 
         for id in source_ids.iter() {
             try!(reg.load(id, false));
@@ -90,7 +91,7 @@ impl<'a> PackageRegistry<'a> {
 
             // Get the summaries
             for summary in (try!(source.list())).iter() {
-                assert!(!dst.contains(summary), "duplicate summaries");
+                assert!(!dst.contains(summary), "duplicate summaries: {}", summary);
                 dst.push(summary.clone());
                 // self.summaries.push(summary.clone());
             }
@@ -106,6 +107,17 @@ impl<'a> PackageRegistry<'a> {
     }
 }
 
+fn dedup(ids: Vec<SourceId>) -> Vec<SourceId> {
+    let mut seen = vec!();
+
+    for id in ids.move_iter() {
+        if seen.contains(&id) { continue; }
+        seen.push(id);
+    }
+
+    seen
+}
+
 impl<'a> Registry for PackageRegistry<'a> {
     fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
         let overrides = try!(self.overrides.query(dep)); // this can never fail in practice
index 6e7192a359a1c79c63f98771e01f1907f7c1cd85..a9aedb7494b2d59ab737d70ab787864a95747fe7 100644 (file)
@@ -61,7 +61,7 @@ pub enum Location {
     Remote(Url),
 }
 
-#[deriving(Clone,PartialEq)]
+#[deriving(Clone, PartialEq, Eq)]
 pub struct SourceId {
     pub kind: SourceKind,
     pub location: Location,
index 91dbc63615981d20498227cb0fbfe1e9e81e9069..e21472af1a736e28e22d0b0467bb3e1881810e04 100644 (file)
@@ -1,8 +1,9 @@
 use std::collections::HashSet;
-use std::io::File;
+use std::io::{File, fs};
 use util;
 use core::{Package,Manifest,SourceId};
-use util::{CargoResult, human, important_paths};
+use util::{CargoResult, human};
+use util::important_paths::find_project_manifest_exact;
 
 pub fn read_manifest(contents: &[u8], source_id: &SourceId)
     -> CargoResult<(Manifest, Vec<Path>)>
@@ -22,26 +23,85 @@ pub fn read_package(path: &Path, source_id: &SourceId)
     Ok((Package::new(manifest, path, source_id), nested))
 }
 
-pub fn read_packages(path: &Path, source_id: &SourceId)
-    -> CargoResult<Vec<Package>>
+pub fn read_packages(path: &Path,
+                     source_id: &SourceId) -> CargoResult<Vec<Package>>
 {
-    return read_packages(path, source_id, &mut HashSet::new());
+    let mut all_packages = Vec::new();
+    let mut visited = HashSet::<Path>::new();
 
-    fn read_packages(path: &Path, source_id: &SourceId,
-                     visited: &mut HashSet<Path>) -> CargoResult<Vec<Package>> {
-        if !visited.insert(path.clone()) { return Ok(Vec::new()) }
+    log!(5, "looking for root package: {}, source_id={}", path.display(), source_id);
+    try!(process_possible_package(path, &mut all_packages, source_id, &mut visited));
 
-        let manifest = try!(important_paths::find_project_manifest_exact(path,
-                                                                         "Cargo.toml"));
-        let (pkg, nested) = try!(read_package(&manifest, source_id));
-        let mut ret = vec![pkg];
+    try!(walk(path, true, |dir| {
+        log!(5, "looking for child package: {}", dir.display());
+        if dir.filename_str() == Some(".git") { return Ok(false); }
+        if dir.join(".git").is_dir() { return Ok(false); }
+        try!(process_possible_package(dir, &mut all_packages, source_id, &mut visited));
+        Ok(true)
+    }));
 
-        for p in nested.iter() {
-            ret.push_all(try!(read_packages(&path.join(p),
-                                            source_id,
-                                            visited)).as_slice());
+    if all_packages.is_empty() {
+        Err(human(format!("Could not find Cargo.toml in `{}`", path.display())))
+    } else {
+        log!(5, "all packages: {}", all_packages);
+        Ok(all_packages)
+    }
+}
+
+fn walk(path: &Path, is_root: bool, callback: |&Path| -> CargoResult<bool>) -> CargoResult<()> {
+    if path.is_dir() {
+        if !is_root {
+            let continues = try!(callback(path));
+            if !continues { log!(5, "Found submodule at {}", path.display()); return Ok(()); }
         }
 
-        Ok(ret)
+        for dir in try!(fs::readdir(path)).iter() {
+            try!(walk(dir, false, |x| callback(x)))
+        }
+    }
+
+    Ok(())
+}
+
+fn process_possible_package(dir: &Path,
+                            all_packages: &mut Vec<Package>,
+                            source_id: &SourceId,
+                            visited: &mut HashSet<Path>) -> CargoResult<()> {
+
+    if !has_manifest(dir) { return Ok(()); }
+
+    let packages = try!(read_nested_packages(dir, source_id, visited));
+    push_all(all_packages, packages);
+
+    Ok(())
+}
+
+fn has_manifest(path: &Path) -> bool {
+    find_project_manifest_exact(path, "Cargo.toml").is_ok()
+}
+
+fn read_nested_packages(path: &Path, source_id: &SourceId,
+                 visited: &mut HashSet<Path>) -> CargoResult<Vec<Package>> {
+    if !visited.insert(path.clone()) { return Ok(Vec::new()) }
+
+    let manifest = try!(find_project_manifest_exact(path, "Cargo.toml"));
+
+    let (pkg, nested) = try!(read_package(&manifest, source_id));
+    let mut ret = vec![pkg];
+
+    for p in nested.iter() {
+        ret.push_all(try!(read_nested_packages(&path.join(p),
+                                        source_id,
+                                        visited)).as_slice());
+    }
+
+    Ok(ret)
+}
+
+fn push_all(set: &mut Vec<Package>, packages: Vec<Package>) {
+    for package in packages.move_iter() {
+        if set.contains(&package) { continue; }
+
+        set.push(package)
     }
 }
index 9b4489d79d69b124c9d3e1f1e968907b1bb48ab9..ef107fca2fe22452a71c55877f41860a25faca4c 100644 (file)
@@ -203,7 +203,6 @@ fn prepare_rustc(root: &Path, target: &Target, crate_types: Vec<&str>,
     build_base_args(&mut args, target, crate_types, cx);
     build_deps_args(&mut args, cx);
 
-
     util::process("rustc")
         .cwd(root.clone())
         .args(args.as_slice())
index c587d00e7986431ec93cff9328d43535e3027310..62370c11dad27dad315763862a292864d27e3a25 100644 (file)
@@ -227,6 +227,7 @@ test!(cargo_compile_git_dep_tag {
       cargo::util::process(project.bin("foo")),
       execs().with_stdout("hello world\n"));
 })
+
 test!(cargo_compile_with_nested_paths {
     let git_project = git_repo("dep1", |project| {
         project
@@ -302,6 +303,79 @@ test!(cargo_compile_with_nested_paths {
       execs().with_stdout("hello world\n"));
 })
 
+test!(cargo_compile_with_meta_package {
+    let git_project = git_repo("meta-dep", |project| {
+        project
+            .file("dep1/Cargo.toml", r#"
+                [project]
+
+                name = "dep1"
+                version = "0.5.0"
+                authors = ["carlhuda@example.com"]
+
+                [[lib]]
+
+                name = "dep1"
+            "#)
+            .file("dep1/src/dep1.rs", r#"
+                pub fn hello() -> &'static str {
+                    "this is dep1"
+                }
+            "#)
+            .file("dep2/Cargo.toml", r#"
+                [project]
+
+                name = "dep2"
+                version = "0.5.0"
+                authors = ["carlhuda@example.com"]
+
+                [[lib]]
+
+                name = "dep2"
+            "#)
+            .file("dep2/src/dep2.rs", r#"
+                pub fn hello() -> &'static str {
+                    "this is dep2"
+                }
+            "#)
+    }).assert();
+
+    let p = project("parent")
+        .file("Cargo.toml", format!(r#"
+            [project]
+
+            name = "parent"
+            version = "0.5.0"
+            authors = ["wycats@example.com"]
+
+            [dependencies.dep1]
+
+            version = "0.5.0"
+            git = "file:{}"
+
+            [dependencies.dep2]
+
+            version = "0.5.0"
+            git = "file:{}"
+
+            [[bin]]
+
+            name = "parent"
+        "#, escape_path(&git_project.root()), escape_path(&git_project.root())))
+        .file("src/parent.rs",
+              main_file(r#""{} {}", dep1::hello(), dep2::hello()"#, ["dep1", "dep2"]).as_slice());
+
+    p.cargo_process("cargo-build")
+        .exec_with_output()
+        .assert();
+
+    assert_that(&p.bin("parent"), existing_file());
+
+    assert_that(
+      cargo::util::process(p.bin("parent")),
+      execs().with_stdout("this is dep1 this is dep2\n"));
+})
+
 test!(cargo_compile_with_short_ssh_git {
     let url = "git@github.com:a/dep";